package org.finesys.common.mp.core.encrypt.core.interceptor;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.finesys.common.mp.core.encrypt.core.annotation.EncryptData;
import org.finesys.common.mp.core.encrypt.core.properties.EncryptProperties;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;

@Slf4j
@RequiredArgsConstructor
@Intercepts(@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }))
public class EncryptInterceptor implements Interceptor {

	private final EncryptProperties encryptProperties;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 获取方法参数
		Object[] args = invocation.getArgs();
		Class<?>[] params = { MappedStatement.class, Object.class };
		// 验证是否符合加密
		if (!encryptProperties.isEnableFieldDecrypt() || args.length != params.length) {
			return invocation.proceed();
		}
		// 获取加密参数
		Object obj = args[1];
		// 加密
		this.aesEncrypt(obj);
		return invocation.proceed();
	}

	private void aesEncrypt(Object obj) {
		if (ObjectUtils.isEmpty(obj)) {
			return;
		}
		// 获取需要加密的字段
		Field[] fields = ReflectUtil.getFields(obj.getClass(),
				field -> Objects.nonNull(field.getAnnotation(EncryptData.class)));
		// 加密
		Arrays.asList(fields)
			.stream()
			.forEach(field -> ReflectUtil.setFieldValue(obj, field,
					this.aseEncryptValue(ReflectUtil.getFieldValue(obj, field))));

	}

	private Object aseEncryptValue(Object fieldValue) {
		// 只对字符串加密
		if (fieldValue instanceof String) {
			AES aes = SecureUtil.aes(encryptProperties.getFieldDecryptKey().getBytes(StandardCharsets.UTF_8));
			return aes.encryptBase64((String) fieldValue);
		}
		return fieldValue;
	}

	@Override
	public Object plugin(Object target) {
		return Interceptor.super.plugin(target);
	}

	@Override
	public void setProperties(Properties properties) {
		Interceptor.super.setProperties(properties);
	}

}
